﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Linq;
using System.Windows.Forms;
using DevExpress.Xpo.Exceptions;
using E4RPTools;
using Fitecom.DataSet;
using Interop.ErpBS800;
using Interop.GcpBE800;
using Interop.StdBE800;

namespace Fitecom
{
    public class Controller
    {
        private const string Product = "Fitecom";
        private OleDbConnection _accessConnection;
        private readonly ErpBS _erp = new ErpBS();
        private string _connectionString;
        private SqlConnection _connection;
        

        public IWin32Window OwnerWindow { get; set; }

        public string LogFile { get; set; }        
        

        private void FixAccessConnection()
        {
            if (_accessConnection.State == ConnectionState.Open)
                _accessConnection.Close();
            _accessConnection.Open();
        }

        private void FixConnection()
        {
            if (_connection.State == ConnectionState.Open)
                _connection.Close();
            _connection.Open();
        }        

        private bool SaveClient(string cliente, string nome, string pais, string vendedor)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.SaveClient(oleDbTransaction, cliente, nome, pais, vendedor);
                oleDbTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível gravar o cliente no access.");
                return false;
            }
        }

        private void SynchronizeClients(string client)
        {
            string filter = "";
            if (client.Length > 0)
                filter += " and Cliente = '" + client + "'";
            StdBELista clients = _erp.Consulta("select * from clientes where CDU_Sincronizado = 0" + filter);
            if (clients.Vazia())
                return;
            clients.NoInicio();
            while (!clients.NoFim())
            {
                string cliente = Convert.ToString(clients.Valor("Cliente"));
                string nome = Convert.ToString(clients.Valor("Nome"));
                string pais = Convert.ToString(clients.Valor("Pais"));
                string vendedor = Convert.ToString(clients.Valor("Vendedor"));
                if (SaveClient(cliente, nome, pais, vendedor))
                    _erp.Comercial.Clientes.ActualizaValorAtributo(cliente, "CDU_Sincronizado", 1);
                clients.Seguinte();
            }
        }

        private void DeleteClient(string client)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.DeleteClient(oleDbTransaction, client);
                oleDbTransaction.Commit();
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível anular o cliente no access.");
            }
        }



        private long GetSupplierId(StdBELista suppliers, string nome)
        {
            try
            {
                return Convert.ToInt64(suppliers.Valor("Fornecedor"));
            }
            catch (Exception e)
            {
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, string.Format("O código do fornecedor '{0}' deve ser um valor inteiro.", nome));
                return -1;
            }

        }

        private bool SaveSupplier(long numfor, string nome, string pais)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.SaveSupplier(oleDbTransaction, numfor, nome, pais);
                oleDbTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível gravar o fornecedor no access.");
                return false;
            }
        }

        private void SynchronizeSuppliers(string supplier)
        {
            string filter = "";
            if (supplier.Length > 0)
                filter += " and Fornecedor = '" + supplier + "'";
            StdBELista suppliers = _erp.Consulta("select * from fornecedores where CDU_Sincronizado = 0" + filter);
            if (suppliers.Vazia())
                return;
            suppliers.NoInicio();
            while (!suppliers.NoFim())
            {
                string nome = Convert.ToString(suppliers.Valor("Nome"));
                long numfor = GetSupplierId(suppliers, nome);
                if (numfor == -1)
                    return;
                string pais = Convert.ToString(suppliers.Valor("Pais"));
                if (SaveSupplier(numfor, nome, pais))
                    _erp.Comercial.Fornecedores.ActualizaValorAtributo(numfor.ToString(), "CDU_Sincronizado", 1);
                suppliers.Seguinte();
            }
        }

        private void DeleteSupplier(string numfor)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.DeleteSupplier(oleDbTransaction, numfor);
                oleDbTransaction.Commit();
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível anular o fornecedor no access.");
            }
        }


        private static double GetDouble(object valor)
        {
            try
            {
                return Convert.ToDouble(valor);
            }
            catch (Exception)
            {
                return 0;
            }

        }

        private static int GetInt(object valor)
        {
            try
            {
                return Convert.ToInt32(valor);
            }
            catch (Exception)
            {
                return 0;
            }

        }

        private bool SaveArticle(string artigo, string descricao, string familia, string subfamilia, double pvp, string composicao, double peso, double comprimento, double largura, string acabamento, 
            string padrao, string cor, string unidadeBase)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.SaveArticle(oleDbTransaction, artigo, descricao, familia, subfamilia, pvp, composicao, peso, comprimento, largura, acabamento, padrao, cor, unidadeBase);
                oleDbTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível gravar o artigo no access.");
                return false;
            }
        }

        private void SynchronizeArticles(string article)
        {
            string filter = "";
            if (article.Length > 0)
                filter += " and a.Artigo = '" + article + "'";
            StdBELista articles = _erp.Consulta(
                @"  select  a.Artigo, Familia, Descricao, SubFamilia, PVP1, CDU_Composicao, PesoLiquido, CDU_Comprimento, CDU_Largura, a.UnidadeBase 
                    from    Artigo a, ArtigoMoeda am 
                    where   a.CDU_Sincronizado = 0
                            and am.Artigo = a.Artigo" + filter);
            if (articles.Vazia())
                return;
            articles.NoInicio();
            while (!articles.NoFim())
            {
                string artigo = Convert.ToString(articles.Valor("Artigo"));
                string familia = Convert.ToString(articles.Valor("Familia"));
                if (familia.ToUpper() == "TECIDO")
                {
                    string[] artigocomposto = artigo.Split('-');
                    if (artigocomposto.Length == 3)
                    {
                        string acabamento = artigocomposto[0];
                        _erp.Comercial.Artigos.ActualizaValorAtributo(artigo, "CDU_Acabamento", acabamento);
                        string padrao = artigocomposto[1];
                        _erp.Comercial.Artigos.ActualizaValorAtributo(artigo, "CDU_Padrao", padrao);
                        string cor = artigocomposto[2];
                        _erp.Comercial.Artigos.ActualizaValorAtributo(artigo, "CDU_Cor", cor);
                        string descricao = Convert.ToString(articles.Valor("Descricao"));
                        string subfamilia = Convert.ToString(articles.Valor("SubFamilia"));
                        double pvp = GetDouble(articles.Valor("PVP1"));
                        string composicao = Convert.ToString(articles.Valor("CDU_Composicao"));
                        double peso = GetDouble(articles.Valor("PesoLiquido"));
                        double comprimento = GetDouble(articles.Valor("CDU_Comprimento"));
                        double largura = GetDouble(articles.Valor("CDU_Largura"));
                        string unidadeBase = Convert.ToString(articles.Valor("UnidadeBase"));
                        if (SaveArticle(artigo, descricao, familia, subfamilia, pvp, composicao, peso, comprimento, largura, acabamento, padrao, cor, unidadeBase))
                            _erp.Comercial.Artigos.ActualizaValorAtributo(artigo, "CDU_Sincronizado", 1);
                    }
                }
                articles.Seguinte();
            }
        }

        private void DeleteArticle(string article)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.DeleteArticle(oleDbTransaction, article);
                oleDbTransaction.Commit();
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível anular o artigo no access.");
            }
        }


        private void SaveCompound(string composicao, string familia, string descricao)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.SaveCompound(oleDbTransaction, composicao, familia, descricao);
                oleDbTransaction.Commit();
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível gravar a composição no access.");
            }
        }

        private ComposicoesDataSet GetCompounds()
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                var dataSet = AccessDatabase.GetCompounds(oleDbTransaction);
                oleDbTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível obter a lista de composições.");
                return new ComposicoesDataSet();
            }
        }

        private static bool ExistsCompound(ComposicoesDataSet.ComposicaoRow compound, StdBELista compounds)
        {
            compounds.NoInicio();
            while (!compounds.NoFim())
            {
                string codigo = Convert.ToString(compounds.Valor("CDU_Codigo"));
                string familia = Convert.ToString(compounds.Valor("CDU_Familia"));
                if (codigo == compound.Codigo && familia == compound.Familia)
                    return true;
                compounds.Seguinte();
            }
            return false;
        }

        private void DeleteCompound(string composicao, string familia)
        {
            FixAccessConnection();
            var oleDbTransaction = _accessConnection.BeginTransaction();
            try
            {
                AccessDatabase.DeleteCompound(oleDbTransaction, composicao, familia);
                oleDbTransaction.Commit();
            }
            catch (Exception e)
            {
                oleDbTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, "Não foi possível eliminar a composição no access.");
            }
        }


        private bool UpdateBatch(string artigo, string lote, DateTime data, string loteObs)
        {
            try
            {
                if (_erp.Comercial.ArtigosLotes.Existe(artigo, lote))
                    return true;
                GcpBEArtigoLote batchArticle = new GcpBEArtigoLote();
                batchArticle.set_Artigo(artigo);
                batchArticle.set_Lote(lote);
                batchArticle.set_DataFabrico(data);
                batchArticle.set_Validade(new DateTime(data.Year, 12, 31));
                batchArticle.set_Observacoes(loteObs);
                batchArticle.set_Activo(true);
                _erp.Comercial.ArtigosLotes.Actualiza(batchArticle);
            }
            catch (Exception e)
            {
                ExceptionPublisher.ShowErrorMessage(OwnerWindow, "Não foi possível registar entrada de stock:" + Environment.NewLine + e.Message, Product);
                return false;
            }
            return true;
        }


        private void UpdateEntradaStock(long numMvt, int numDoc, string serie)
        {
            StdBECampo campoChave = new StdBECampo { Nome = "CDU_NumMvt", Tipo = EnumTipoCampo.tcBigInt, Objecto = numMvt, Valor = numMvt };
            var camposChave = new StdBECamposChave();
            camposChave.CamposChave.Insere(campoChave);
            _erp.TabelasUtilizador.ActualizaValorAtributo("TDU_EntradaStock", camposChave, "CDU_Sincronizado", true);
            _erp.TabelasUtilizador.ActualizaValorAtributo("TDU_EntradaStock", camposChave, "CDU_NumDocOriginado", numDoc);
            _erp.TabelasUtilizador.ActualizaValorAtributo("TDU_EntradaStock", camposChave, "CDU_TipoDocOriginado", "EOP");
            _erp.TabelasUtilizador.ActualizaValorAtributo("TDU_EntradaStock", camposChave, "CDU_SerieDocOriginado", serie);
        }

        private void UpdateArtigoArmazem(string artigo, string armazem, string lote, string loteObs, Single peso, Single quantidadeComDefeito, int notas, Single pesoEmbalagem, string unidadePeso, int loteFabrico, Single largura)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                Database.UpdateArtigoArmazem(sqlTransaction, artigo, armazem, lote, loteObs, peso, quantidadeComDefeito, notas, pesoEmbalagem, unidadePeso, loteFabrico, largura);
                sqlTransaction.Commit();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível actualuzar os campos de utilizador da linha de stock.");
            }
        }

        private void UpdateLinhaStock(GcpBELinhaDocumentoStock stockLine, string loteObs, Single peso, Single quantidadeComDefeito, int notas, Single pesoEmbalagem, string unidadePeso, int loteFabrico, Single largura)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                Database.UpdateStockLine(sqlTransaction, stockLine.get_IdLinha(), loteObs, peso, quantidadeComDefeito, notas, pesoEmbalagem, unidadePeso, loteFabrico, largura);
                sqlTransaction.Commit();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível atualizar os campos de utilizador da linha de stock.");
            }
        }

        private GcpBEDocumentoStock SaveStockInput(long numMvt, DateTime data, string artigo, string descricao, string lote, string armazem, double quantidade, double precUnit, string unidade)
        {
            try
            {
                GcpBELinhaDocumentoStock stockLine = new GcpBELinhaDocumentoStock();
                stockLine.set_Artigo(artigo);
                stockLine.set_Descricao(descricao);
                stockLine.set_Armazem(armazem);
                stockLine.set_Lote(lote);
                stockLine.set_Quantidade(quantidade);
                stockLine.set_PrecUnit(precUnit);
                stockLine.set_Unidade(unidade);
                stockLine.set_DataStock(data);
                //
                GcpBELinhasDocumentoStock stockLines = new GcpBELinhasDocumentoStock();
                stockLines.Insere(stockLine);
                //
                DateTime dateTime = new DateTime(data.Year, data.Month, data.Day);
                GcpBEDocumentoStock stock = new GcpBEDocumentoStock() { ID = Guid.NewGuid().ToString() };
                //stock.set_Entidade(cliente.ToString());
                stock.set_Serie(_erp.Comercial.Series.DaSerieDefeito("S", "EOP", data));
                stock.set_Moeda(_erp.Contexto.MoedaEuro);
                stock.set_Linhas(stockLines);
                stock.set_DataDoc(data);
                stock.set_DataUltimaActualizacao(data);
                stock.set_Tipodoc("EOP");
                stock = _erp.Comercial.Stocks.PreencheDadosRelacionados(stock);
                stock.set_DataDoc(data);                
                _erp.Comercial.Stocks.Actualiza(stock);
                _erp.Comercial.Stocks.ActualizaValorAtributo(stock.get_Tipodoc(), stock.get_NumDoc(), stock.get_Filial(), stock.get_Serie(), "CDU_NumMvt", numMvt.ToString());
                UpdateEntradaStock(numMvt, stock.get_NumDoc(), stock.get_Serie());
                return stock;
            }
            catch (Exception e)
            {
                ExceptionPublisher.ShowErrorMessage(OwnerWindow, "Não foi possível registar entrada de stock para o movimento " + numMvt + ":" + Environment.NewLine + e.Message, Product);
                return null;
            }
        }

        private bool CanUpdateCustomOrderPreparation(string serieEnc)
        {
            if (string.IsNullOrEmpty(serieEnc)) return false;
            if (serieEnc.Trim() == "0") return false;
            
            return true;            
        }

        private GcpBELinhaDocumentoVenda GetOrder(long numMvt, string filial, string serieEnc, string numEnc, string artigo)
        {
            var vendas = _erp.Comercial.Vendas;
            string tipoDoc = "ENC";
            int numDoc = GetInt(numEnc);
            if (!vendas.Existe(ref filial, ref tipoDoc, ref serieEnc, ref numDoc))
            {
                ExceptionPublisher.ShowErrorMessage(OwnerWindow, "Não existe Encomenda de Cliente com o número de encomenda '" + numEnc + "' para o movimento '" + numMvt + "'.", Product);
                return null;
            }
            GcpBEDocumentoVenda documentoVenda = vendas.Edita(ref filial, ref tipoDoc, ref serieEnc, ref numDoc);
            foreach (Object entry in documentoVenda.get_Linhas())
            {
                if (entry == null)
                    continue;
                var linha = (GcpBELinhaDocumentoVenda)entry;
                if (linha.get_Artigo() == artigo) 
                    return linha;
            }
            ExceptionPublisher.ShowErrorMessage(OwnerWindow, "Na Encomenda de Cliente '" + numEnc + "' não existe o artigo '" + artigo + "' para o movimento '" + numMvt + "'.", Product);            
            return null;
        }

        private static string GetLocalization(ErpBS erpBs, string article, string warehouse)
        {
            if (!erpBs.Comercial.Artigos.Existe(article))
                return "";
            GcpBEArtigoArmazens gcpBeArtigoArmazens = erpBs.Comercial.ArtigosArmazens.ListaArtigosArmazens(article);
            foreach (GcpBEArtigoArmazem gcpBeArtigoArmazem in gcpBeArtigoArmazens)
                if (gcpBeArtigoArmazem.get_Localizacao() != "" && gcpBeArtigoArmazem.get_Armazem() == warehouse)
                    return gcpBeArtigoArmazem.get_Localizacao();
            GcpBEArtigo gcpBeArtigo = erpBs.Comercial.Artigos.Edita(article);
            if (gcpBeArtigo.get_LocalizacaoSugestao() != "")
                return gcpBeArtigo.get_LocalizacaoSugestao();
            foreach (GcpBEArtigoArmazem gcpBeArtigoArmazem in gcpBeArtigoArmazens)
                if (gcpBeArtigoArmazem.get_Localizacao() != "")
                    return gcpBeArtigoArmazem.get_Localizacao();
            return "";
        }

        private Guid UpdateCustomOrderPreparation(long numMvt, string filial, string serieEnc, string numEnc, string lote, string armazem, double quantidade, string artigo, DateTime data, string unidade)
        {
            Guid lineId = Guid.Empty;
            if (string.IsNullOrEmpty(numEnc))
            {
                ExceptionPublisher.ShowErrorMessage(OwnerWindow, "Não existe número de encomenda da Preparação de Encomenda de Cliente para o movimento '" + numMvt + "'.", Product);
                return lineId;
            }
            GcpBELinhaDocumentoVenda linhaEncomenda = GetOrder(numMvt, filial, serieEnc, numEnc, artigo);
            if (linhaEncomenda == null) return lineId;
            string tipoDoc = "PEC";
            int numDoc = GetInt(numEnc);
            var vendas = _erp.Comercial.Vendas;
            if (!vendas.Existe(ref filial, ref tipoDoc, ref serieEnc, ref numDoc))
            {
                ExceptionPublisher.ShowErrorMessage(OwnerWindow, "Não existe Preparação de Encomenda de Cliente com o número de encomenda '" + numEnc + "' para o movimento '" + numMvt + "'.", Product);
                return lineId;
            }
            
            //Também deve ir buscar o preço, o tipo de iva 
            
            lineId = Guid.NewGuid();
            try
            {
                GcpBEDocumentoVenda documentoVenda = vendas.Edita(ref filial, ref tipoDoc, ref serieEnc, ref numDoc);
                GcpBELinhaDocumentoVenda linhaDocumentoVenda = new GcpBELinhaDocumentoVenda();
                linhaDocumentoVenda.set_IdLinha(lineId.ToString());
                linhaDocumentoVenda.set_Lote(lote);
                linhaDocumentoVenda.set_Armazem(armazem);
                linhaDocumentoVenda.set_Quantidade(quantidade);
                linhaDocumentoVenda.set_QuantReservada(quantidade);               
                linhaDocumentoVenda.set_TipoLinha("10");
                linhaDocumentoVenda.set_Artigo(artigo);
                linhaDocumentoVenda.set_Unidade(unidade);
                linhaDocumentoVenda.set_DataEntrega(data);
                linhaDocumentoVenda.set_DataStock(data);
                linhaDocumentoVenda.set_PrecUnit(linhaEncomenda.get_PrecUnit());
                linhaDocumentoVenda.set_Descricao(linhaEncomenda.get_Descricao());
                linhaDocumentoVenda.set_Desconto1(linhaEncomenda.get_Desconto1());
                linhaDocumentoVenda.set_Desconto2(linhaEncomenda.get_Desconto2());
                linhaDocumentoVenda.set_Desconto3(linhaEncomenda.get_Desconto3());
                linhaDocumentoVenda.set_DescontoComercial(linhaEncomenda.get_DescontoComercial());
                linhaDocumentoVenda.set_Comissao(linhaEncomenda.get_Comissao());
                linhaDocumentoVenda.set_Vendedor(linhaEncomenda.get_Vendedor());
                linhaDocumentoVenda.set_PercIvaDedutivel(linhaEncomenda.get_PercIvaDedutivel());
                linhaDocumentoVenda.set_PercIncidenciaIVA(linhaEncomenda.get_PercIncidenciaIVA());
                linhaDocumentoVenda.set_CodIva(linhaEncomenda.get_CodIva());
                linhaDocumentoVenda.set_CodIvaEcotaxa(linhaEncomenda.get_CodIvaEcotaxa());
                linhaDocumentoVenda.set_CodIvaIEC(linhaEncomenda.get_CodIvaIEC());
                linhaDocumentoVenda.set_TaxaIva(linhaEncomenda.get_TaxaIva());
                linhaDocumentoVenda.set_TaxaIvaEcotaxa(linhaEncomenda.get_TaxaIvaEcotaxa());
                linhaDocumentoVenda.set_Localizacao(linhaEncomenda.get_Localizacao() != "" ? linhaEncomenda.get_Localizacao() : GetLocalization(_erp, artigo, armazem));
                GcpBELinhasDocumentoVenda linhasDocumentoVenda = documentoVenda.get_Linhas();
                linhasDocumentoVenda.Insere(linhaDocumentoVenda);
                documentoVenda.set_Linhas(linhasDocumentoVenda);
                vendas.Actualiza(documentoVenda);
                return lineId;
            }
            catch (Exception e)
            {
                ExceptionPublisher.ShowErrorMessage(OwnerWindow,
                                                    "Não foi possível atualizar a Preparação de Encomenda de Cliente com o número de encomenda '" + numEnc + "' para o movimento '" + numMvt + "'."
                                                    + Environment.NewLine + e.Message, Product);                
                return Guid.Empty;
            }            
        }

        private void UpdateCustomOrderPreparationLine(string artigo, string armazem, string lote, Guid customOrderPreparationLineId)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                Database.UpdateSellItem(sqlTransaction, customOrderPreparationLineId.ToString(), artigo, armazem, lote);
                sqlTransaction.Commit();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível atualizar os campos de utilizador da linha de Preparação de Encomenda de Cliente.");
            }
        }

        //deve criar adicionar essa linha ao respectivo documento ‘PEC’ com os campos lote,armazém,quantidade,quandidade_reservada e os campos adicionais da tabela ‘ArtigoArmazem’ preenchidos
       
        //private void InsertGramagem(string lote, string tipoDocumento, string serie, int numeroDocumento, DateTime dataDocumento, double peso, double quantidade, double factor, string notas)
        //{
        //    StdBECampo campoLote = new StdBECampo { Nome = "CDU_Lote", Tipo = EnumTipoCampo.tcVarchar, Objecto = lote, Valor = lote };
        //    StdBECampo campoTipoDocumento = new StdBECampo { Nome = "CDU_TipoDocumento", Tipo = EnumTipoCampo.tcVarchar, Objecto = tipoDocumento, Valor = tipoDocumento };
        //    StdBECampo campoSerie = new StdBECampo { Nome = "CDU_Serie", Tipo = EnumTipoCampo.tcVarchar, Objecto = serie, Valor = serie };
        //    StdBECampo campoNumeroDocumento = new StdBECampo { Nome = "CDU_NumeroDocumento", Tipo = EnumTipoCampo.tcInt, Objecto = numeroDocumento, Valor = numeroDocumento };
        //    StdBECampo campoDataDocumento = new StdBECampo { Nome = "CDU_DataDocumento", Tipo = EnumTipoCampo.tcDateTime, Objecto = dataDocumento, Valor = dataDocumento };
        //    StdBECampo campoPeso = new StdBECampo { Nome = "CDU_Peso", Tipo = EnumTipoCampo.tcFloat, Objecto = peso, Valor = peso };
        //    StdBECampo campoQuantidade = new StdBECampo { Nome = "CDU_Quantidade", Tipo = EnumTipoCampo.tcFloat, Objecto = quantidade, Valor = quantidade };
        //    StdBECampo campoFactor = new StdBECampo { Nome = "CDU_Factor", Tipo = EnumTipoCampo.tcFloat, Objecto = factor, Valor = factor };
        //    StdBECampo campoNotas = new StdBECampo { Nome = "CDU_Notas", Tipo = EnumTipoCampo.tcVarchar, Objecto = notas, Valor = notas };
            
        //    StdBECampos campos = new StdBECampos();
        //    campos.Insere(campoLote);
        //    campos.Insere(campoTipoDocumento);
        //    campos.Insere(campoSerie);
        //    campos.Insere(campoNumeroDocumento);
        //    campos.Insere(campoDataDocumento);
        //    campos.Insere(campoPeso);
        //    campos.Insere(campoQuantidade);
        //    campos.Insere(campoFactor);
        //    campos.Insere(campoNotas);

        //    StdBERegistoUtil registoUtil = new StdBERegistoUtil();
        //    registoUtil.set_Campos(campos);
        //    _erp.TabelasUtilizador.Actualiza("TDU_Gramagem", registoUtil);
        //}

        private void SaveStock(StdBELista stocks)
        {
            long numMvt = Convert.ToInt64(stocks.Valor("CDU_NumMvt"));
            string artigo = Convert.ToString(stocks.Valor("CDU_Artigo"));
            string descricao = Convert.ToString(stocks.Valor("CDU_Descricao"));
            string unidade = Convert.ToString(stocks.Valor("CDU_Unidade"));
            double precUnit = Convert.ToDouble(stocks.Valor("CDU_PrecUnit"));
            string lote = Convert.ToString(stocks.Valor("CDU_Lote"));
            string loteObs = Convert.ToString(stocks.Valor("CDU_LoteObs"));
            string armazem = Convert.ToString(stocks.Valor("CDU_Armazem"));
            double quantidade = Convert.ToDouble(stocks.Valor("CDU_Quantidade"));
            Single peso = Convert.ToSingle(stocks.Valor("CDU_Peso"));
            Single quantidadeComDefeito = Convert.ToSingle(stocks.Valor("CDU_QuantidadeComDefeito"));
            int notas = Convert.ToInt32(stocks.Valor("CDU_Notas"));
            Single pesoEmbalagem = Convert.ToSingle(stocks.Valor("CDU_PesoEmbalagem"));
            string unidadePeso = Convert.ToString(stocks.Valor("CDU_UnidadePeso"));
            int loteFabrico = Convert.ToInt32(stocks.Valor("CDU_LoteFabrico"));
            Single largura = Convert.ToSingle(stocks.Valor("CDU_Largura"));
            DateTime data = Convert.ToDateTime(stocks.Valor("CDU_Data"));
            string numEnc = Convert.ToString(stocks.Valor("CDU_NumEnc"));
            string serieEnc = Convert.ToString(stocks.Valor("CDU_SerieEnc"));
            _erp.IniciaTransaccao();

            if(!_erp.Comercial.Artigos.Existe(artigo))
            {
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, new Exception(), LogFile, Product, string.Format("O artigo '{0}' não é válido, no movimento '{1}'.", artigo, numMvt));
                _erp.DesfazTransaccao();
                return;                                
            }

            if (!UpdateBatch(artigo, lote, data, loteObs))
            {
                _erp.DesfazTransaccao();
                return;                
            }
            GcpBEDocumentoStock stock = SaveStockInput(numMvt, data, artigo, descricao, lote, armazem, quantidade, precUnit, unidade);
            if(stock == null)
            {
                _erp.DesfazTransaccao();
                return;
            }
            bool canUpdateCustomOrderPreparation = CanUpdateCustomOrderPreparation(serieEnc);
            Guid customOrderPreparationLineId = Guid.Empty;
            if(canUpdateCustomOrderPreparation)
            {
                customOrderPreparationLineId = UpdateCustomOrderPreparation(numMvt, stock.get_Filial(), serieEnc, numEnc, lote, armazem, quantidade, artigo, data, unidade);
                if (customOrderPreparationLineId == Guid.Empty)
                {
                    _erp.DesfazTransaccao();
                    return;
                }                
            }
            _erp.TerminaTransaccao();
            UpdateArtigoArmazem(artigo, armazem, lote, loteObs, peso, quantidadeComDefeito, notas, pesoEmbalagem, unidadePeso, loteFabrico, largura);
            if(canUpdateCustomOrderPreparation)
                UpdateCustomOrderPreparationLine(artigo, armazem, lote, customOrderPreparationLineId);
            foreach (Object entry in stock.get_Linhas())
            {
                if (entry == null)
                    continue;
                var linha = (GcpBELinhaDocumentoStock)entry;
                UpdateLinhaStock(linha, loteObs, peso, quantidadeComDefeito, notas, pesoEmbalagem, unidadePeso, loteFabrico, largura);
            }                                   
        }


        private bool SellDocumentUpdatedValid(GcpBEDocumentoVenda gcpBeDocumentoVenda)
        {
            if (gcpBeDocumentoVenda.get_Tipodoc() == "EOP")
                return false;
            return true;
        }

        private bool SellDocumentItemUpdatedValid(GcpBELinhaDocumentoVenda gcpBeLinhaDocumentoVenda)
        {
            GcpBEArtigo gcpBeArtigo = _erp.Comercial.Artigos.Edita(gcpBeLinhaDocumentoVenda.get_Artigo());
            if (gcpBeArtigo == null)
                return false;
            if(gcpBeArtigo.get_Familia().ToUpper() != "TECIDO")
                return false;
            return true;
        }

        private GcpBEArtigoArmazem GetWarehouseArticle(string artigo, string armazem, string lote, string localizacao)
        {
            if (!_erp.Comercial.ArtigosArmazens.Existe(artigo, armazem, lote, localizacao))
                return null;
            return _erp.Comercial.ArtigosArmazens.Edita(artigo, armazem, lote, localizacao);
        }

        private double GetQuantity(GcpBEDocumentoVenda documentoVenda, string artigo, string armazem, string lote, string localizacao)
        {
            double quantity = 0;
            foreach (Object entry in documentoVenda.get_Linhas())
            {
                if (entry == null)
                    continue;
                var line = (GcpBELinhaDocumentoVenda)entry;
                if (line.get_Artigo() == artigo && line.get_Armazem() == armazem && line.get_Lote() == lote && line.get_Localizacao() == localizacao)
                    quantity += line.get_Quantidade();
            }
            return quantity;
        }

        private string GetStockMovType(GcpBEDocumentoVenda sell)
        {
            GcpBETabVenda gcpBeTabVenda = _erp.Comercial.TabVendas.Edita(sell.get_Tipodoc());
            string movtype = gcpBeTabVenda.get_TipoMovStock();
            return movtype;
        }

        private bool OutputSellItemUpdated(GcpBEDocumentoVenda sell, GcpBELinhaDocumentoVenda sellItem)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                GcpBEArtigoArmazem warehouseArticle = GetWarehouseArticle(sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao());
                if (warehouseArticle == null) return true;
                if (warehouseArticle.get_StkActual() <= 0)
                    Database.ResetWarehouseArticle(sqlTransaction, sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao());
                else
                {
                    double quantity = warehouseArticle.get_StkActual() + GetQuantity(sell, sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao());
                    double weight = Database.GetWarehouseArticleWeight(sqlTransaction, sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao());
                    Double unitWeight = weight / quantity;
                    double newWeight =  sell.get_Tipodoc() == "NC" ? Math.Round(weight + unitWeight * sellItem.get_Quantidade(), 2) : Math.Round(weight - unitWeight * sellItem.get_Quantidade(), 2);
                    Database.UpdateWarehouseArticleWeight(sqlTransaction, sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao(), newWeight);
                    Database.UpdateSellItem(sqlTransaction, sellItem.get_IdLinha(), sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao());
                    //Database.UpdateSellItemWeitghtWithEntryStock(sqlTransaction, sellItem.get_IdLinha(), sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote());
                    Database.UpdateSellItemWeitght(sqlTransaction, sellItem.get_IdLinha(), Math.Round(unitWeight * sellItem.get_Quantidade(), 2));
                }
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível actualizar os campos de utilizador da linha de venda de saída.");
                return false;
            }
        }

        private bool InputSellItemUpdated(GcpBELinhaDocumentoVenda sellItem)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                GcpBEArtigoArmazem warehouseArticle = GetWarehouseArticle(sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao());              
                if (warehouseArticle == null) return true;
                Database.UpdateWarehouseWithSellItem(sqlTransaction, sellItem.get_IdLinha(), sellItem.get_Artigo(), sellItem.get_Armazem(), sellItem.get_Lote(), sellItem.get_Localizacao());
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível actualizar os campos de utilizador da linha de venda de entrada.");
                return false;
            }
        }

        private void UpdateSellItems(string sellId)
        {
            if (!_erp.Comercial.Vendas.ExisteID(sellId)) return;
            try
            {
                GcpBEDocumentoVenda sell = _erp.Comercial.Vendas.EditaID(sellId);
                string stockMovType = GetStockMovType(sell);
                if(!SellDocumentUpdatedValid(sell))
                    return;
                foreach (Object entry in sell.get_Linhas())
                {
                    if (entry == null)
                        continue;
                    var sellItem = (GcpBELinhaDocumentoVenda) entry;
                    if (!SellDocumentItemUpdatedValid(sellItem))
                        continue;
                    if (stockMovType == "S" && !OutputSellItemUpdated(sell, sellItem)) return;
                    if (stockMovType == "E" && !InputSellItemUpdated(sellItem)) return;
                }
            }
            catch (Exception e)
            {
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível actualizar os campos de utilizador da linha de venda.");
            }
        }


        private List<StockLine> GetStockLines(string stockId)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                var stockLines = Database.GetStockLines(sqlTransaction, stockId);
                sqlTransaction.Commit();
                return stockLines;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível obter as linhas de stock.");
                return new List<StockLine>();
            }
        }

        private bool StockDocumentItemUpdatedValid(StockLine stockLine)
        {
            GcpBEArtigo gcpBeArtigo = _erp.Comercial.Artigos.Edita(stockLine.Artigo);
            if (gcpBeArtigo == null)
                return false;
            if (gcpBeArtigo.get_Familia().ToUpper() != "TECIDO")
                return false;
            return true;
        }

        private double GetWarehouseArticleStock(string artigo, string armazem, string lote, string localizacao)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                double stock = Database.GetWarehouseArticleStock(sqlTransaction, artigo, armazem, lote, localizacao);
                sqlTransaction.Commit();
                return stock;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível obter o stock do artigo.");
                return 0;
            }
        }

        private List<ArticleStock> GetStocks(List<StockLine> stockLines)
        {
            var stocks = new List<ArticleStock>();
            foreach (StockLine stockLine in stockLines)
            {
                if (!StockDocumentItemUpdatedValid(stockLine))
                    continue;
                var stocksLine =
                    stocks.FirstOrDefault(
                        articleStock =>
                        articleStock.Artigo == stockLine.Artigo && articleStock.Armazem == stockLine.Armazem &&
                        articleStock.Lote == stockLine.Lote && articleStock.Localizacao == stockLine.Localizacao);
                if (stocksLine == null)
                {
                    stocksLine = new ArticleStock() { Artigo = stockLine.Artigo, Armazem = stockLine.Armazem, Lote = stockLine.Lote, Localizacao = stockLine.Localizacao, OldStock = 0 };
                    stocks.Add(stocksLine);
                }
                if (stockLine.EntradaSaida == "E") stocksLine.OldStock += stockLine.Quantidade;
                if (stockLine.EntradaSaida == "S") stocksLine.OldStock -= stockLine.Quantidade;
            }
            foreach(ArticleStock stockLine in stocks)
            {
                stockLine.NewStock = GetWarehouseArticleStock(stockLine.Artigo, stockLine.Armazem, stockLine.Lote, stockLine.Localizacao);
                stockLine.OldStock = stockLine.NewStock - stockLine.OldStock;
            }
            return stocks;
        }

        private bool OutputStockItemUpdated(StockLine stockLine, List<ArticleStock> stocks)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                var stocksLine =
                    stocks.FirstOrDefault(
                        articleStock =>
                        articleStock.Artigo == stockLine.Artigo && articleStock.Armazem == stockLine.Armazem &&
                        articleStock.Lote == stockLine.Lote && articleStock.Localizacao == stockLine.Localizacao);
                if (stocksLine.NewStock <= 0)
                    Database.ResetWarehouseArticle(sqlTransaction, stockLine.Artigo, stockLine.Armazem, stockLine.Lote, stockLine.Localizacao);
                else
                {
                    double weight = Database.GetWarehouseArticleWeight(sqlTransaction, stockLine.Artigo, stockLine.Armazem, stockLine.Lote, stockLine.Localizacao);
                    Double unitWeight = weight / stocksLine.OldStock;
                    Database.UpdateWarehouseArticleWeight(sqlTransaction, stockLine.Artigo, stockLine.Armazem, stockLine.Lote, stockLine.Localizacao, Math.Round(unitWeight * stocksLine.NewStock, 2));
                    Database.UpdateStockItem(sqlTransaction, stockLine.IdLinha.ToString(), stockLine.Artigo, stockLine.Armazem, stockLine.Lote, stockLine.Localizacao);
                    Database.UpdateStockItemWeitght(sqlTransaction, stockLine.IdLinha.ToString(), Math.Round(unitWeight * stockLine.Quantidade, 2));
                }
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível actualizar os campos de utilizador da linha de venda de saída.");
                return false;
            }
        }

        private bool InputStockItemUpdated(StockLine stockLine)
        {
            FixConnection();
            var sqlTransaction = _connection.BeginTransaction();
            try
            {
                Database.UpdateWarehouseWithSellItem(sqlTransaction, stockLine.IdLinha.ToString(), stockLine.Artigo, stockLine.Armazem, stockLine.Lote, stockLine.Localizacao);
                sqlTransaction.Commit();
                return true;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível actualizar os campos de utilizador da linha de venda de entrada.");
                return false;
            }
        }

        private void UpdateStockItems(string stockId)
        {
            if (!_erp.Comercial.Stocks.ExisteID(stockId)) return;
            try
            {
                List<StockLine> stockLines = GetStockLines(stockId);
                List<ArticleStock> stocks = GetStocks(stockLines); 
                foreach (StockLine stockLine in stockLines)
                {
                    if (!StockDocumentItemUpdatedValid(stockLine))
                        continue;
                    if (stockLine.EntradaSaida == "S" && !OutputStockItemUpdated(stockLine, stocks)) return;
                    if (stockLine.EntradaSaida == "E" && !InputStockItemUpdated(stockLine)) return;
                }
            }
            catch (Exception e)
            {
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Não foi possível actualizar os campos de utilizador da linha de stock.");
            }
        }


        public bool IsAccessConnectionValid()
        {
            var accessDatabase = ConfigurationManager.AppSettings["FicheiroAccess"];
            _connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data source={0};User Id=admin;Password=;", accessDatabase);
            try
            {
                _accessConnection = new OleDbConnection(_connectionString);
                _accessConnection.Open();
            }
            catch (Exception e)
            {
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Utilizador sem acesso à base de dados de Access.");
                return false;                
            }
            return true;
        }

        public bool IsUserValid(string userName, string password)
        {
            var dataSource = ConfigurationManager.AppSettings["FonteDados"];
            var databaseName = ConfigurationManager.AppSettings["NomeBaseDados"];
            var connectionString = String.Format("Data Source={0};Initial Catalog={1};User ID={2};Password={3};MultipleActiveResultSets=True;", dataSource, databaseName, userName, password);
            try
            {
                _connection = new SqlConnection(connectionString);
                _connection.Open();
            }
            catch (Exception e)
            {
                ExceptionPublisher.PublishAndShowErrorMessage(null, e, LogFile, Product, "Utilizador sem acesso à base de dados.");
                return false;
            }
            return true;
        }

        public bool OpenPrimaveraCompany()
        {
            var user = "";
            var company = "";
            try
            {
                company = ConfigurationManager.AppSettings["PrimaveraEmpresa"];
                user = ConfigurationManager.AppSettings["PrimaveraUtilizador"];
                var password = ConfigurationManager.AppSettings["PrimaveraSenha"];
                var platform = EnumTipoPlataforma.tpProfissional;
                StdBETransaccao transaction = null;
                var instance = "DEFAULT";
                var primaryMode = true;
                //ExceptionPublisher.Publish(LogFile, new Exception("OpenPrimaveraCompany: Antes"));
                _erp.AbreEmpresaTrabalho(ref platform, ref company, ref user, ref password, ref transaction, ref instance, ref primaryMode);
                //ExceptionPublisher.Publish(LogFile, new Exception("OpenPrimaveraCompany: Depois"));
            }
            catch (Exception e)
            {
                ExceptionPublisher.PublishAndShowErrorMessage(OwnerWindow, e, LogFile, Product, string.Format("Não foi possível abrir a empresa '{0}', para o utilizador '{1}'.", company, user));
                return false;
            }
            return true;
        }

        public void ClosePrimaveraCompany()
        {
            _erp.FechaEmpresaTrabalho();
        }

        public void SynchronizeClients()
        {
            SynchronizeClients("");
        }

        public void SynchronizeArticles()
        {
            SynchronizeArticles("");
        }

        public void SynchronizeSuppliers()
        {
            SynchronizeSuppliers("");            
        }

        public void SynchronizeCompounds()
        {
            StdBELista compounds = _erp.Consulta("select * from TDU_Composicoes");
            if (compounds.Vazia())
                return;
            var originalCoumpounds = GetCompounds();
            compounds.NoInicio();
            while (!compounds.NoFim())
            {
                string codigo = Convert.ToString(compounds.Valor("CDU_Codigo"));
                string descricao = Convert.ToString(compounds.Valor("CDU_Descricao"));
                string familia = Convert.ToString(compounds.Valor("CDU_Familia"));
                SaveCompound(codigo, familia, descricao);
                compounds.Seguinte();
            }
            foreach (var compound in originalCoumpounds.Composicao)
                if (!ExistsCompound(compound, compounds))
                    DeleteCompound(compound.Codigo, compound.Familia);
        }
        
        public void SynchronizeStocks()
        {
            StdBELista stocks = _erp.Consulta("select * from TDU_EntradaStock where CDU_Sincronizado = 0");
            if (stocks.Vazia())
                return;
            while (!stocks.NoFim())
            {
                SaveStock(stocks);
                stocks.Seguinte();
            }
        }

        public void Import(string action, string document)
        {
            switch (action)
            {
                case "AnularArtigo":
                    DeleteArticle(document);
                    break;
                case "ActualizarArtigo":
                    SynchronizeArticles(document);
                    break;
                case "AnularCliente":
                    DeleteClient(document);
                    break;
                case "ActualizarCliente":
                    SynchronizeClients(document);
                    break;
                case "AnularFornecedor":
                    DeleteSupplier(document);
                    break;
                case "ActualizarFornecedor":
                    SynchronizeSuppliers(document);
                    break;
                case "ActualizarItemsVenda":
                    UpdateSellItems(document);
                    break;
                case "ActualizarItemsStock":
                    UpdateStockItems(document);
                    break;
            }
            
        }

        public void CloseConnections()
        {
            _accessConnection.Close();
            _connection.Close();
            ClosePrimaveraCompany();                  
        }
    }
}
